#include "stdafx.h"
#include "Export.h"
#include "WordProxy.h"
#include "Utils.h"
#include "FileNameUtils.h"
#include <iostream>
#include <memory>

/////////////////////// CBaseExport ///////////////////////

#pragma warning(push)
#pragma warning(disable: 4244)
void CBaseExport::ResizeCompletion(const SIZE& OriginalSize, SIZE& NewSize)
{
	_ASSERT( (NewSize.cx != 0) || (NewSize.cy != 0) );
	_ASSERT( (OriginalSize.cx != 0) && (OriginalSize.cy != 0) );
	if( NewSize.cx == 0 )
	{
		NewSize.cx = OriginalSize.cx * ((float)NewSize.cy / (float)OriginalSize.cy);
		return;
	}
	if( NewSize.cy == 0 )
	{
		NewSize.cy = OriginalSize.cy * ((float)NewSize.cx / (float)OriginalSize.cx);
		return;
	}
}

void CBaseExport::PieceSizeCompletion(const SIZE& OriginalSize, SIZE& PieceSize)
{
	_ASSERT( (PieceSize.cx != 0) || (PieceSize.cy != 0) );
	_ASSERT( (OriginalSize.cx != 0) && (OriginalSize.cy != 0) );
	if( PieceSize.cx == 0 )
	{
		PieceSize.cx = OriginalSize.cx * ((float)PieceSize.cy / (float)OriginalSize.cy);
		return;
	}
	if( PieceSize.cy == 0 )
	{
		PieceSize.cy = OriginalSize.cy * ((float)PieceSize.cx / (float)OriginalSize.cx);
		return;
	}
}
#pragma warning(pop)

const char* cUnableToOpenImage = "Unable to open image.";
const char* cLoadingImageMsg = "Wait ...";

bool CBaseExport::LoadAndResizeImage(CImageSplitter& Splitter, const CProgramOptions& Options)
{
	try
	{
		tstring InputFileName;
		Options.GetInputFileName(InputFileName);
		std::cout << cLoadingImageMsg << std::endl;
		Splitter.OpenImage(InputFileName.c_str() );
		
		Splitter.SetJPEGQuailty( Options.GetCompressQuality() );

		if( Options.NeedResize() )
		{
			SIZE NewSize = {0};
			SIZE OriginalSize = {0};
			Options.GetNewSize(NewSize);
			OriginalSize.cx = Splitter.GetImageWidthInPixels();
			OriginalSize.cy = Splitter.GetImageHeightInPixels();

			CBaseExport::ResizeCompletion(OriginalSize, NewSize);
			Splitter.ResizeImage(NewSize.cx, NewSize.cy);
		}
	}
	catch(EUnableToOpenImage& )
	{
		std::cout << cUnableToOpenImage << std::endl;
		return false;
	}
	return true;
}

void CBaseExport::PreparePathToUse(tstring& Path, PathType_t PathType)
{
	TCHAR FullPath[_MAX_PATH];

	ZeroMemory( FullPath, sizeof(TCHAR)*_MAX_PATH);
	if( _tfullpath(FullPath, Path.c_str(), _MAX_PATH) == NULL )
		throw ECheckPathError(cpeUnableToResolvePath, Path);
	
	tstring sFullPath = FullPath;
	tstring FullPathWithoutDelim;

	ExcludeTrailingPathDelimiter(sFullPath, FullPathWithoutDelim);
	if( (PathType == pthtFile) && (FullPath != FullPathWithoutDelim) )
		throw ECheckPathError(cpeWrongPathType, sFullPath);
	
	if( FileExists(sFullPath) )
	{
		if(PathType == pthtFile)
		{
			if( IsDirectory(sFullPath))
				throw ECheckPathError(cpeWrongPathType, sFullPath);
			if( !CheckWriteAccessToFile(sFullPath) )
				throw ECheckPathError(cpeAccessDenied, sFullPath);
		}
	}
	else
	{
		if( PathType == pthtDirectory )
		{
			if (!ForceDirectories( sFullPath ))
				throw ECheckPathError(cpeUnableToCreatePath, sFullPath);	
		}
		else
		{
			tstring FilePath;
			ExtractFilePath(sFullPath, FilePath);
			if (!ForceDirectories( FilePath ))
				throw ECheckPathError(cpeUnableToCreatePath, FilePath);
			if( !CheckWriteAccessToFile(sFullPath) )
				throw ECheckPathError(cpeAccessDenied, sFullPath);
		}
	}
	Path = sFullPath;
}

bool CBaseExport::CheckWriteAccessToFile(const tstring& Path)
{
	bool bAlredyExists = FileExists( Path );
	HANDLE hFile = CreateFile(Path.c_str(), FILE_WRITE_DATA, 0, NULL, CREATE_ALWAYS, 0, NULL);
	
	if ( INVALID_HANDLE_VALUE == hFile )
			return false;

	CloseHandle( hFile );
	
	if( !bAlredyExists )
		DeleteFile( Path.c_str() );	
	return true;
}


void CBaseExport::CorrectExt(tstring& OutputPath, const CProgramOptions& Options)
{
	tstring ProperExt;

	if( Options.GetExportType() == exp2Word )
	{
		if( !HasDocExt(OutputPath) )
		{
			GetDefaultDocExt(ProperExt);
			OutputPath.append(_T("."));
			OutputPath.append(ProperExt);
		}
	}
	else
	{
		if( Options.NeedSplit() ) // output path must be a name of directory
			return;
		if( !(HasJPEGExt(OutputPath) || HasPNGExt(OutputPath)) )
		{
			tstring InputPath;
			Options.GetInputFileName(InputPath);
			if( HasJPEGExt(InputPath) )
			{
				GetDefaultJPEGExt(ProperExt);
				OutputPath.append(_T("."));
				OutputPath.append(ProperExt);
				return ;
			}
			if( HasPNGExt(InputPath) )
			{
				GetDefaultPNGExt(ProperExt);
				OutputPath.append(_T("."));
				OutputPath.append(ProperExt);
				return ;
			}
			// do nothing with unknown extension. CImageIOHelper reject it.
		}
	}
}

bool CBaseExport::GetOutputPath(const CProgramOptions& Options, tstring& OutputPath)
{
	Options.GetOutputFileName(OutputPath);
	
	// TODO: check and add extension if necessary
	PathType_t PathType;
	if( Options.GetExportType() == exp2Word)
	{
		PathType = pthtFile;
	}
	else
	{
		PathType = Options.NeedSplit()? pthtDirectory:pthtFile;
	}
	bool bResult = false;
	try
	{
		PreparePathToUse( OutputPath, PathType);
		if( PathType == pthtFile)
			CorrectExt(OutputPath, Options);
		bResult = true;
	}
	catch(const ECheckPathError& Error)
	{
		ProcessCheckPathError(Error);
	}
	return bResult;
}

wchar_t* cUnableToResolvePath = L"Unable to get full path from ";
wchar_t* cUnableToCreatePath = L"Unable to create path ";
wchar_t* cWrongPathType = L"Wrong path type (directory/file) ";
wchar_t* cAccessDenied = L"Access denied to ";

void CBaseExport::ProcessCheckPathError(const ECheckPathError& Error)
{
	tstring HelpString;
	Error.GetHelpString(HelpString);

	switch( Error.GetContext() )
	{
	case cpeUnableToResolvePath: 
		std::wcout << cUnableToResolvePath << HelpString << std::endl;
		break;
	case cpeUnableToCreatePath:
		std::wcout << cUnableToCreatePath << HelpString << std::endl;
		break;
	case cpeWrongPathType:
		std::wcout << cWrongPathType << HelpString << std::endl;
		break;
	case cpeAccessDenied:
		std::wcout << cAccessDenied << HelpString << std::endl;
	}
}

char* cUnableToInitOLEMsg = "OleInitialize failed.";
char* cWordInitializationFailure = "Initialization of MSWord failed.";
char* cInvocationError = "Error on accessing to method or property.";
char* cUnableToSavePiece = "Unable to save piece.";

void CWordExport::ExportIthPieceToWord(CImageSplitter& Splitter, CWordProxy& Word, int i)
{
	tstring TempFileName;
	if( !GetTempFileName(TempFileName) )
		throw EUnableToSaveImage();

	std::cout << "Getting " << i << " piece ... " << std::endl; // Temp
	Splitter.GetPiece(i, TempFileName.c_str() );
	tstring PieceFileName;
	if( !ChangeFileExtAndRename(TempFileName, _T("jpg"), PieceFileName) )
		throw EUnableToSaveImage();
	std::cout << "Exporting " << i << " piece to word ... " << std::endl;
	Word.PastePicture(PieceFileName.c_str() );
	DeleteFile(PieceFileName.c_str() );
}

void CWordExport::ExportToWord(CImageSplitter& Splitter, const tstring& OutputFile)
{
	CWordProxy Word;
	if( OleInitialize(NULL) != S_OK )
	{
		std::cout << cUnableToInitOLEMsg << std::endl;
		return;
	}
	try
	{
		Word.Initialize();
		Word.NewDocument();
		DOCUMENT_PAGE_SIZE PageSize = {0};
		Word.GetPageSizeInPt(PageSize);
		DOCUMENT_MARGINS Margins = {0};
		Word.GetMarginsInPt(Margins);
		LONG ActualWidth = Word.PointsToPixels(PageSize.Width - (Margins.Left + Margins.Right), false);
		LONG ActualHeight = Word.PointsToPixels(PageSize.Height - (Margins.Bottom + Margins.Top) - 4 , true);
		Splitter.SplitImage(ActualWidth, ActualHeight);

		ExportIthPieceToWord(Splitter, Word, Splitter.GetPieceCount());
		for( int i = Splitter.GetPieceCount() - 1; i > 0; i--)
		{	
			SIZE PieceSize = {0};
			Splitter.GetPieceSize(i, PieceSize);
			if( PieceSize.cy < ActualHeight ) // TODO: test it
				Word.PageBreakAndUP();

			ExportIthPieceToWord(Splitter, Word, i);		
		}
		Word.SaveAs(OutputFile.c_str());
	}
	catch(EIinitializationError)
	{
		std::cout << cWordInitializationFailure << std::endl;
	}
	catch(EInvocationError)
	{
		std::cout << cInvocationError << std::endl;
	}
	catch(EUnableToSaveImage)
	{
		std::cout << cUnableToSavePiece << std::endl;
	}
	Word.Finalize();
	OleUninitialize();
}

void CFileExport::ExportToFile(CImageSplitter &Splitter, const tstring& OutputFile, const SIZE& RawPieceSize, const tstring& OutputFilePattern, int MaxOutputFileLen)
{

	if( (RawPieceSize.cx != 0) || (RawPieceSize.cy != 0) )
	{
		SIZE PieceSize = RawPieceSize;
		SIZE ImageSize = {0};
		ImageSize.cy = Splitter.GetImageHeightInPixels();
		ImageSize.cx = Splitter.GetImageWidthInPixels();

		PieceSizeCompletion(ImageSize, PieceSize);
		Splitter.SplitImage(PieceSize.cx, PieceSize.cy);
		
		try
		{
			for( int i = 1; i<=Splitter.GetPieceCount(); i++)
			{
				// TODO: construct name for the I-th piece, this is temp code
				tstring IthPieceName;
				ExcludeTrailingPathDelimiter(OutputFile, IthPieceName);

				IthPieceName.append(_T("\\"));

				std::auto_ptr<TCHAR> IthPieceFileName( new TCHAR[MaxOutputFileLen] );
				ZeroMemory(IthPieceFileName.get(), sizeof(TCHAR)*MaxOutputFileLen);

				_stprintf_s(IthPieceFileName.get(), MaxOutputFileLen, OutputFilePattern.c_str(), i );
				IthPieceName.append( IthPieceFileName.get() );
				
				if( !CheckWriteAccessToFile( IthPieceFileName.get() ) )
					throw EUnableToSaveImage();
				std::cout << "Getting " << i << " piece ... " << std::endl; // Temp
				Splitter.GetPiece(i, IthPieceName.c_str() );
				std::cout << " Saved. " << std::endl; // Temp
			}
		}
		catch(EUnableToSaveImage)
		{
			std::cout << cUnableToSavePiece << std::endl;
		}
	}
	else
	{
		try
		{
			Splitter.GetPiece(0, OutputFile.c_str() );
		}
		catch(EUnableToSaveImage)
		{
			std::cout << cUnableToSavePiece << std::endl;
		}
	}
}